package com.future.spring.utils;

import com.future.spring.annotation.Bean;
import com.future.spring.annotation.Component;
import com.future.spring.exception.BeanDefinitionException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * @author weber
 * @Description:
 * @Date 2025/7/30 14:37
 */
public class ClassUtils {


    public static <A extends Annotation> A getAnnotation(Class<?> target, Class<A> annoClass) {
        A annotation = target.getAnnotation(annoClass);
        for (Annotation anno : target.getAnnotations()) {
            Class<? extends Annotation> annoType = anno.annotationType();
            if (!annoType.getPackageName().equals("java.lang.annotation")) {
                A found = getAnnotation(annoType, annoClass);
                if (found != null) {
                    if (annotation != null) {
                        throw new BeanDefinitionException("Duplicate @" + annoClass.getSimpleName() + " found on class " + target.getSimpleName());
                    }
                }
            }
        }
        return annotation;
    }

    public static String getBeanName(Class<?> clazz) {
        String name = "";
        Component annotation = clazz.getAnnotation(Component.class);
        if (annotation != null) {
            name = annotation.value();
        } else {
            // 没找到@Component，继续在其他注解找@Componet
            for (Annotation anno : clazz.getAnnotations()) {
                if (getAnnotation(anno.annotationType(), Component.class) != null) {
                    try {
                        name = (String) anno.annotationType().getMethod("value").invoke(anno);
                    } catch (Exception e) {
                        throw new BeanDefinitionException(String.format("cannot get annotation value. ", e));
                    }
                }
            }
        }
        if (name.isEmpty()) {
            name = clazz.getSimpleName();
            // 转成驼峰命名
            name = name.substring(0, 1).toLowerCase() + name.substring(1);
        }
        return name;
    }

    public static String getBeanName(Method method) {
        Bean annotation = method.getAnnotation(Bean.class);
        String name = annotation.value();
        if (name.isEmpty()) {
            name = method.getName();
        }
        return name;
    }


    public static Method findAnnotationMethod(Class<?> clazz, Class<? extends Annotation> annoClass) {
        // try get declared method
        List<Method> ms = Arrays.stream(clazz.getDeclaredMethods()).filter(m -> m.isAnnotationPresent(annoClass)).map(m -> {
            if (m.getParameterCount() != 0) {
                throw new BeanDefinitionException(String.format("Method '%s' with @%s must not have argument: %s ", m.getName(), annoClass.getSimpleName(), clazz.getName()));
            }
            return m;
        }).toList();
        if (ms.isEmpty()) {
            return null;
        }
        if (ms.size() == 1) {
            return ms.getFirst();
        }
        throw new BeanDefinitionException(String.format("Multiple @%s methods found on class %s", annoClass.getSimpleName(), clazz.getName()));
    }

    public static <A extends Annotation> A getAnnotation(Annotation[] annos, Class<A> annoClass) {
        for (Annotation anno : annos) {
            if (annoClass.isInstance(anno)) {
                return (A) anno;
            }
        }
        return null;
    }
}
